/***********************************************************************
 SlateInfo.cpp - Defines the mechanism to parse the slate info XML
 	file and store its contents.

 Created 2006.06.11 by Warren Young

 Copyright (c) 2006 by Pixel Corps.  This program may be used under
 the terms of the X11 license, a copy of which should have accompanied
 this program, in the LICENSE file.
***********************************************************************/

#include "SlateInfo.h"

#include <libgen.h>

#include <fstream>
#include <iostream>

#include <ctype.h>

using namespace std;


//// Handle_* //////////////////////////////////////////////////////////

static void
Handle_ElementStart(void* pUserData, const XML_Char* name,
		const XML_Char** atts)
{
	SlateInfo* pSI = static_cast<SlateInfo*>(pUserData);
	pSI->StartElement((const char*)name);
}

static void
Handle_ElementEnd(void* pUserData, const XML_Char* name)
{
	SlateInfo* pSI = static_cast<SlateInfo*>(pUserData);
	pSI->EndElement((const char*)name);
}

static void
Handle_CharacterData(void* pUserData, const XML_Char* s, int len)
{
	SlateInfo* pSI = static_cast<SlateInfo*>(pUserData);
	pSI->SetChars((const char*)s, len);
}


//// ctor //////////////////////////////////////////////////////////////

SlateInfo::SlateInfo(const char* filename) :
sFileName_(filename)
{
	parser_ = XML_ParserCreate("UTF-8");
	XML_SetUserData(parser_, this);
	XML_SetElementHandler(parser_, Handle_ElementStart,
			Handle_ElementEnd);
	XML_SetCharacterDataHandler(parser_, Handle_CharacterData);

	ifstream ifs(filename);
	char ac[4096];
	while (ifs) {
		ifs.read(ac, sizeof(ac));
		XML_Parse(parser_, ac, ifs.gcount(), ifs.eof());
		//cout << "Read " << ifs.gcount() << " bytes from " <<
		//		FileName() << endl;
	}
}


//// dtor //////////////////////////////////////////////////////////////

SlateInfo::~SlateInfo()
{
	XML_ParserFree(parser_);
}


//// DumpDoc ///////////////////////////////////////////////////////////
// Show all the info found in the document

void
SlateInfo::DumpDoc()
{
	cout << "Document data:" << endl;

	map<string, string>::iterator it1;
	for (it1 = DocData_.begin(); it1 != DocData_.end(); ++it1) {
		cout << "    " << it1->first << " = \"" << it1->second <<
				'\"' << endl;
	}
	
	cout << "Local paths:" << endl;
	vector<string>::iterator it2;
	for (it2 = LocalPaths_.begin(); it2 != LocalPaths_.end(); ++it2) {
		cout << "    " << *it2 << endl;
	}
	
	cout << "Remote paths:" << endl;
	for (it2 = RemotePaths_.begin(); it2 != RemotePaths_.end(); ++it2) {
		cout << "    " << *it2 << endl;
	}
}


//// EndElement ////////////////////////////////////////////////////////

void
SlateInfo::EndElement(const char* pcElement)
{
	if (strcmp(pcElement, "localpath") == 0) {
		LocalPaths_.push_back(DocData_[string(pcElement)]);
		DocData_.erase("localpath");
	}

	if (strcmp(pcElement, "remotepath") == 0) {
		RemotePaths_.push_back(DocData_[string(pcElement)]);
		DocData_.erase("remotepath");
	}

	ElementStack_.pop();
	//cout << DocData_[string(pcElement)].length() << " chars</" <<
	//		pcElement << '>' << endl;
}


//// Get* //////////////////////////////////////////////////////////////
// Return key info discovered in XML file

const char*
SlateInfo::GetArtist() const
{
	return DocData_[string("artist")].c_str();
}

const char*
SlateInfo::GetDate() const
{
	time_t t = time(0);
	struct tm* ptm = localtime(&t);
	static char ac[11];
	snprintf(ac, sizeof(ac), "%04d.%02d.%02d",
			ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday);
	return ac;
}

const char*
SlateInfo::GetElement() const
{
	return DocData_["element"].c_str();
}

const char*
SlateInfo::GetFirstFile() const
{
	return basename(LocalPaths_[0].c_str());
}

const char*
SlateInfo::GetNotes() const
{
	return DocData_["notes"].c_str();
}

const char*
SlateInfo::GetProject() const
{
	return DocData_["project"].c_str();
}

const char*
SlateInfo::GetScene() const
{
	return DocData_["scene"].c_str();
}


//// Indent ////////////////////////////////////////////////////////////

void
SlateInfo::Indent()
{
	for (size_t i = 0; i < ElementStack_.size(); ++i) {
		cout << "    ";
	}
}


//// StartElement //////////////////////////////////////////////////////

void
SlateInfo::StartElement(const char* pcElement)
{
	//Indent(); cout << '<' << pcElement << '>';
	ElementStack_.push(string(pcElement));
}


//// SetChars //////////////////////////////////////////////////////////

void
SlateInfo::SetChars(const char* pcText, int nLen)
{
	DocData_[ElementStack_.top()] += string(pcText, nLen);
}


//// TrimAllWS /////////////////////////////////////////////////////////
// Goes through all the strings we retrieved from the document and trims
// leading and trailing whitespace from them.

void
SlateInfo::TrimAllWS()
{
	map<string, string>::iterator it1;
	for (it1 = DocData_.begin(); it1 != DocData_.end(); ++it1) {
		TrimWS(it1->second);
	}

	vector<string>::iterator it2;
	for (it2 = LocalPaths_.begin(); it2 != LocalPaths_.end(); ++it2) {
		TrimWS(*it2);
	}
	for (it2 = RemotePaths_.begin(); it2 != RemotePaths_.end(); ++it2) {
		TrimWS(*it2);
	}
}


//// TrimWS ////////////////////////////////////////////////////////////
// Removes leading and trailing whitespace from the given string.

void
SlateInfo::TrimWS(string& s)
{
	// Trim leading space
	size_t i;
	//cout << '\"' << s << '\"' << endl;
	for (i = 0; i < s.length() && isspace(s.at(i)); ++i) {
		//cout << "trim start: " << i << '/' << s.length() << endl;
	}
	if (i > 0) {
		s = s.substr(i);
	}

	// Trim trailing space, if string is not now blank.
	if (s.length() > 0) {
		for (i = s.length() - 1; i >= 0 && isspace(s.at(i)); --i) {
			//cout << "trim end: " << i << '/' << s.length() << endl;
		}
		if (i >= 0) {
			s = s.substr(0, i + 1);
		}
		else {
			s.clear();
		}
	}

	//cout << '\"' << s << '\"' << endl;
}

